package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"

	//"net/url"
	"strings"
	"time"
)

var URL string = "http://127.0.0.1:9200"
var EnvName string = "environment_sla"
var UserName string = ""
var PassWord string = ""
var COPYINDEX string = `{
  "conflicts": "proceed",
  "source": {
    "index": "%s"
  },
  "dest": {
    "index": "%s",
    "op_type": "create"
  }
}`

var ALIAS string = `{
    "actions" : [
        { "remove" : { "index" : "%s", "alias" : "%s" } },
        { "add" : { "index" : "%s", "alias" : "%s" } }
    ]
}`

var PIPELINE string =`PUT _ingest/pipeline/create_time_pipeline
{
  "pipeline": {
    "processors": [
      {
        "script": {
          "lang": "painless",
          "source": """DateFormat df = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ssX\"); df.setTimeZone(TimeZone.getTimeZone(\"Asia/Shanghai\")); Date date = new Date(); ctx._source.update_time = df.format(date);"""
        }
      }
    ]
  }
}`


var TEMPLATE string = `{
  "index_patterns": ["environment_sla_*"],
  "settings": {
    "analysis": {
      "analyzer": {
        "slash": {
          "type": "pattern",
          "pattern": "/"
        },
        "comma": {
          "type": "pattern",
          "pattern": "\\."
        }
      }
    }
  },
  "mappings" : {
    "properties" : {
      "@timestamp":{
        "format":"strict_date_optional_time||epoch_millis",
        "type":"date"
        "enabled":true
      },
      "update_time" : {
        "type" : "date"
      },
      "hostname" : {
         "type" : "text",
         "fields" : {
           "keyword" : {
             "type" : "keyword",
             "ignore_above" : 128
           }
         },
         "analyzer" : "comma",
         "search_analyzer": "comma"
       },
      "node" : {
         "type" : "text",
         "fields" : {
           "keyword" : {
             "type" : "keyword",
             "ignore_above" : 512
           }
         },
         "analyzer" : "comma",
         "search_analyzer": "comma"
       },
      "basic" : {
         "properties" : {
           "core" : {
             "type" : "keyword"
           },
           "cpu" : {
             "type" : "short"
           },
           "disk" : {
             "type" : "short"
           },
           "ip" : {
             "type" : "ip"
           },
           "machinetype" : {
             "type" : "keyword"
           },
           "mem" : {
             "type" : "short"
           },
           "region" : {
             "type" : "keyword"
           }
         }
       },
      "corn" : {
         "properties" : {
           "info" : {
             "type" : "text",
             "fields" : {
               "keyword" : {
                 "type" : "keyword",
                 "ignore_above" : 512
               }
             },
             "analyzer" : "whitespace"
           },
           "user" : {
             "type" : "keyword"
           }
         }
       },
      "cronnum" : {
         "type" : "short"
       },
      "generalcheck" : {
         "properties" : {
           "checkid" : {
             "type" : "long"
           },
           "checkstatus" : {
             "type" : "boolean"
           },
           "name" : {
             "type" : "text",
             "analyzer" : "ik_smart"
           },
           "output" : {
             "type" : "text",
             "fields" : {
               "keyword" : {
                 "type" : "keyword",
                 "ignore_above" : 1024
               }
             },
             "analyzer" : "ik_smart"
           },
           "time" : {
              "type" : "date",
              "format" : "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis",
              "locale":"zh_CN",
              "doc_values":true
            }
         }
       },
      "personalcheck" : {
         "properties" : {
           "checkid" : {
             "type" : "long"
           },
           "checkstatus" : {
             "type" : "boolean"
           },
           "name" : {
             "type" : "text",
             "fields" : {
               "keyword" : {
                 "type" : "keyword",
                 "ignore_above" : 128
               }
             },
             "analyzer" : "ik_smart"
           },
           "output" : {
             "type" : "text",
             "fields" : {
               "keyword" : {
                 "type" : "keyword",
                 "ignore_above" : 1024
               }
             },
             "analyzer" : "ik_smart"
           },
           "time" : {
              "type" : "date",
              "format" : "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis",
              "locale":"zh_CN",
              "doc_values":true
            }
         }
       },
      "service" : {
         "properties" : {
           "level" : {
            "type" : "integer"
           },
           "name" : {
             "type" : "keyword"
           },
           "checkid" : {
             "type" : "integer"
           },
           "path" : {
             "type" : "text",
             "fields" : {
               "keyword" : {
                 "type" : "keyword",
                 "ignore_above" : 256
               }
             },
             "analyzer" : "slash"
           },
           "bin" : {
             "type" : "text",
             "fields" : {
               "keyword" : {
                 "type" : "keyword",
                 "ignore_above" : 256
               }
             },
             "analyzer" : "slash"
           },
           "conf" : {
             "type" : "text",
             "fields" : {
               "keyword" : {
                 "type" : "keyword",
                 "ignore_above" : 256
               }
             },
             "analyzer" : "slash"
           },
           "data" : {
             "type" : "text",
             "fields" : {
               "keyword" : {
                 "type" : "keyword",
                 "ignore_above" : 256
               }
             },
             "analyzer" : "slash"
           },
           "port" : {
             "type" : "integer"
           },
           "changenum" : {
             "type" : "integer"
           },
           "md5" : {
             "type" : "keyword"
           },
           "procinfo" : {
             "type" : "text",
             "fields" : {
               "keyword" : {
                 "type" : "keyword",
                 "ignore_above" : 256
               }
             },
             "analyzer" : "simple"
           },
           "procnum" : {
             "type" : "short"
           },
           "time" : {
             "type" : "date",
             "format" : "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis",
             "locale":"zh_CN",
             "doc_values":true
           },
           "version" : {
             "type" : "keyword"
           },
           "output" : {
             "type" : "text",
             "fields" : {
               "keyword" : {
                 "type" : "keyword",
                 "ignore_above" : 1024
               }
             },
             "analyzer" : "ik_smart"
           },
           "checkstatus" : {
             "type" : "boolean"
           }
         }
       },
      "machinestatus" : {
         "type" : "short"
       },
      "personalstatus" : {
         "type" : "short"
       },
      "servicestatus" : {
         "type" : "short"
       },
      "generalstatus" : {
         "type" : "short"
       },
      "team" : {
         "type" : "text",
         "fields" : {
           "keyword" : {
             "type" : "keyword",
             "ignore_above" : 256
           }
         }
       },
      "util" : {
         "properties" : {
           "cpu" : {
             "type" : "float"
           },
           "disk" : {
             "type" : "float"
           },
           "mem" : {
             "type" : "float"
           }
         }
       },
      "labels": {
         "type" : "nested"
       }
    }
  }  
}`

var UpdateTime string = `{
  "script": {
    "lang": "painless",
    "source": "DateFormat df = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ssX\"); df.setTimeZone(TimeZone.getTimeZone(\"Asia/Shanghai\")); Date date = new Date(); ctx._source.update_time = df.format(date);"
  },
  "query": {
    "match_all": {}
  }
}`

var EventName string = "environment_event"
var EnvironmentEvent string =`{
  "aliases": {},
  "mappings": {
    "properties": {
      "hostname": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 128
          }
        },
        "analyzer": "comma"
      },
      "info": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "ip": {
        "type": "ip"
      },
      "level": {
        "type": "keyword"
      },
      "node": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 512
          }
        },
        "analyzer": "comma"
      },
      "output": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 1024
          }
        },
        "analyzer": "ik_smart"
      },
      "time": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
      }
    }
  }
}`

func ControlIndex(method string, esurl string, body string) (response []byte, err error) {
  client := &http.Client{Timeout: time.Second * 60}

  var resq io.Reader = nil
  if body != "" {
    resq = strings.NewReader(body)
  }
	req, err := http.NewRequest(method, esurl, resq)
	req.Header.Set("Content-Type", "application/json")
  if UserName != "" && PassWord != "" {
    req.SetBasicAuth(UserName,PassWord)
  }
  
	var resp *http.Response
	response = []byte{}
	resp, err = client.Do(req)
	if err != nil {
		fmt.Printf("control index failed, server: %s, err: %v", esurl, err)
		return
	}

	if resp.StatusCode != 200 {
		fmt.Printf("control index failed, statuscode not 200:%d server: %s, err: %v\n", resp.StatusCode, esurl, err)
		fmt.Println(resp)
		return
	}
	if resp.Body != nil {
		defer resp.Body.Close()
		response, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Println("control index response, cannot read response %v", esurl, err)
			return
		}
		fmt.Println("control index response: ", string(response))
	}
	return
}

func CreateEnvironmentTemplate() error {
  url := fmt.Sprintf("%s/_template/%s", URL, EnvName)
  _, err := ControlIndex("PUT", url, TEMPLATE)
  return err
}

func CreateEnvironmentIndex() error {
	today := time.Now().Format("2006.01.02")
	fmt.Println("today index: ", today)

	indexName := fmt.Sprintf("%s_%s", EnvName, today)
	fmt.Println("index index: ", indexName)

	url := fmt.Sprintf("%s/%s", URL, indexName)
	_, err := ControlIndex("PUT", url, "")
	return err
}

func UpdateEnvironmentTime() error {
  url := fmt.Sprintf("%s/%s/_update_by_query", URL, EnvName)
  _, err := ControlIndex("POST", url, UpdateTime)
  return err
}

func CopyDocToEnvironment(yesterday string) error {
	now := time.Now()
	today := now.Format("2006.01.02")	

	today = fmt.Sprintf("%s_%s", EnvName, today)
	fmt.Println("today index: ", today)

  if yesterday == "" {
    yesterday = now.AddDate(0, 0, -1).Format("2006.01.02")
    yesterday = fmt.Sprintf("%s_%s", EnvName, yesterday)
  }
	fmt.Println("yesterday index: ", yesterday)

	copycommand := fmt.Sprintf(COPYINDEX, yesterday, today)
	fmt.Println("copy command: ", copycommand)

	url := fmt.Sprintf("%s/%s", URL, "_reindex?refresh&wait_for_completion=false")
	_, err := ControlIndex("POST", url, copycommand)
	return err
}

func GetYesterDayIndex() (indexname string) {
	url := fmt.Sprintf("%s/%s", URL, EnvName)
	body, err := ControlIndex("GET", url, "")
	if err != nil {
		fmt.Println("request index failed:", err)
		return ""
	}
	var r map[string]interface{}
	if err := json.Unmarshal(body, &r); err != nil {
		fmt.Println("unmarshal index failed:", err)
		return ""
	}
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("revocer:", err)
		}
	}()

	if len(r) != 1 {
		fmt.Println("CANNOT find index")
		return ""
	}

	for indexname = range r {
    fmt.Println("yersterday index is:", indexname)
		return indexname
	}

	fmt.Println("CANNOT find index")
	return ""
}
func CheckTask() error {
	url := fmt.Sprintf("%s/%s", URL, "_tasks?detailed=true&actions=*reindex&human")
	tmp := map[string]interface{}{}
	var i int
	for {
		if i > 3600 {
			fmt.Println("check wait too much time:", i)
		}
		i++
		time.Sleep(3 * time.Second)
		resp, err := ControlIndex("GET", url, "")
		if err != nil {
			fmt.Println("check job failed: ", err)
			return err
		}

		err = json.Unmarshal(resp, &tmp)
		if err != nil {
			fmt.Println("unmarshal failed: ", err)
			return err
		}

		if task, ok := tmp["nodes"].(map[string]interface{}); ok {
			if len(task) != 0 {
				fmt.Println("still have job, wait: ", task)
				continue
			}
			fmt.Println("node job wait, break!")
			return nil
		}
		fmt.Println("cannot find nodes: ", tmp)
		continue

	}
	return nil

}

func RemoveAlias(yesterday string) error {
	now := time.Now()
	today := now.Format("2006.01.02")

	today = fmt.Sprintf("%s_%s", EnvName, today)
	fmt.Println("today index: ", today)

	if yesterday == "" {
		yesterday := now.AddDate(0, 0, -1).Format("2006.01.02")
		yesterday = fmt.Sprintf("%s_%s", EnvName, yesterday)
	}
	fmt.Println("yesterday index: ", yesterday)

	body := fmt.Sprintf(ALIAS, yesterday, EnvName, today, EnvName)
	fmt.Println("alias command: ", body)

	url := fmt.Sprintf("%s/%s", URL, "_aliases")
	_, err := ControlIndex("POST", url, body)
	if err != nil {
		fmt.Println("check job failed: ", err)
		return err
	}
	return nil
}

func main() {
	fmt.Println("start index control", time.Now().Format("2006-01-02T15:04:05+08"))
  
	fmt.Println("--------------------get environment index-------------------")
	yesterday := GetYesterDayIndex()

	fmt.Println("---------------------start create index---------------------")
	
  if err := CreateEnvironmentIndex(); err != nil {
		fmt.Println("create index failed:", err)
		return
	}
	fmt.Println("---------------------copy index doc-------------------------")
	if err := CopyDocToEnvironment(yesterday); err != nil {
		fmt.Println("copy index failed:", err)
		return
	}
	fmt.Println("---------------------check copy job-------------------------")
	if err := CheckTask(); err != nil {
		fmt.Println("check job failed:", err)
		return
	}

	fmt.Println("---------------------remove alias -------------------------")
	if err := RemoveAlias(yesterday); err != nil {
		fmt.Println("remove alias failed:", err)
		return
	}
  
  fmt.Println("---------------------update doc time-------------------------")
  if err := UpdateEnvironmentTime(); err != nil {
    fmt.Println("update_time  change failed:", err)
    return
  }
	fmt.Println("index control done!")
  
}


